/*
 * @Author: wangming
 * @Date: 2021-05-14 14:55:05
 * @LastEditors: wangming
 * @LastEditTime: 2022-08-02 10:46:37
 * @Description: 自定义样式  以及自定义脚本
 */
import store from '@/store'
import { cloneDeep } from 'lodash'
import router from '@/router'
import Modal from '@/pageComponents/main/previewTemplate/index.js'
import { Message, MessageBox, Loading } from 'element-ui'
import { postHmhttprequestdefinitionExecute, postHmhttprequestdefinitionExecutestream, postHmhttprequestdefinitionHancodecall } from '@/apis/paas/HmHttpRequestDefinitionController.js'
import { getHmprocessStartformkey } from '@/apis/paas/HmProcessController.js'
import { postHmprinttemplatePreviewByKey } from "@/apis/paas/HmPrintTemplateController.js";
import { getHmdictionaryOptions } from '@/apis/paas/HmDictionaryController.js'
import vuexstore from '@/store/index'
import { downloadFileStream } from '@/utils/util'
import { transform as babelTransform } from "@babel/standalone";
import { SYSTEM_EVENT_ENUM } from './enum'
const presetjsx = require("@vue/babel-preset-jsx");
import i18n from '@/i18n.js'
import jsbarcode from 'jsbarcode'
/**
 * 页面样式
 */
export class Stylee {
  constructor(css, id = 'dycssId') {
    this.seyleId = 'css_' + id
    this.stylee = this.init()
    this.stylee.innerHTML = css
  }
  init() {
    const dom = document.getElementById(this.seyleId)
    if (dom) {
      return dom
    } else {
      const stylee = document.createElement('style')
      stylee.id = this.seyleId
      document.getElementsByTagName('head').item(0).appendChild(stylee)
      return stylee
    }
  }
  clearDom() {
    if (this.stylee) {
      let style = document.getElementById(this.seyleId);
      style && style.remove();
    }
  }
}
/**
 * 页面事件中心
 */
export class Events {
  constructor({ json, template, pagekeys, composerEvents, parentContent }) {
    this.composerEvents = composerEvents || []
    this.vmOberver = new Oberver()
    //存放所有公共事件函数
    this.exports = {}
    //当前页面所有组件key和实例对象
    this.vms = { ...(parentContent?.vms || {}) }
    // 初始化页面脚本工具
    this.context = new Context({
      template: parentContent?.template || template,//  //所有模态框
      vms: this.vms,
      vmOberver: this.vmOberver,
      catchMap: parentContent?.catchMap || new Map(),// 初始化全局缓存
      pagekeys: [...pagekeys, ...(parentContent?.pagekeys || [])]
    })
    try {
      this.initServe(json)
    } catch (error) {
      console.error(error)
    }
  }
  runSysEvent(event) {
    let { callback } = SYSTEM_EVENT_ENUM.find(i => i.value === event.eventKey)
    callback(this.context, event)
  }
  async runEvents(events, params) {
    let __compnent_identify = params.key || params?._this?.config?.key
    this.context.__compnent_identify = __compnent_identify
    for (const v of events) {
      if (v.composeType === 'logic') {
        let { eventJs } = this.composerEvents.find(i => i.composerKey === v.composerKey) || {}
        try {
          eventJs = decodeURIComponent(eventJs)
        } catch (error) { }
        let eventFun = new Function(eventJs.replace(/export(\sasync)?/, 'return async'))()
        await eventFun(this.context, params.values === undefined ? params._this.value : params.values, params.index)
      } else if (v.composeType === 'system') {
        v.systemEvent.forEach(i => {
          this.runSysEvent(i)
        })
      } else if (this.exports[v.eventName]) {
        await this.exports[v.eventName](
          this.context,
          params.values === undefined ? params._this.value : params.values, params.index
        )
      }
    }
  }
  /**
   * 是否存在函数名称
   * @param {*} fName
   * @param {*} json
   */
  static hasFunction(json, name) {
    if (!json) return
    const namelist = json.match(/export\sfunction\s\S+\(/g).map((i) => {
      return i.replace(/export\sfunction\s(\S+)\(/, '$1')
    })
    return namelist.indexOf(name) > -1
  }
  /**
  * 调用指定函数名称的函数
  * @param {*} fName
  * @param {*} json
  */
  static addFunctionJSON(js, eventName, arg = []) {
    try {
      js = decodeURIComponent(js)
    } catch (error) { }
    if (!Events.hasFunction(js, eventName)) {
      let options = ['context', ...arg]
      const fun = `export function ${eventName}(${options
        }) { } `
      return encodeURIComponent(fun + '\n' + js)
    } else {
      return js
    }
  }
  /**
   * 脚本初始化
   * @param {} json  代码
   * @returns
   */
  initServe(json) {
    if (!json) return
    try {
      json = decodeURIComponent(json)
    } catch (error) { }
    json = json.replace(/export async function|async export function/g, 'export function')
    var newjson = json.replace(/(export((?!export)[\s\S])+await)|export\s+function/g, (res) => {
      let restr = res.indexOf('await') > -1 ? 'export async function' : 'export function'
      return res.replace(/export\s+function/, restr)
    })
    const jsModule = babelTransform(newjson, {
      presets: ['env', presetjsx],
    }).code
    const fun = new Function('exports', jsModule)
    fun(this.exports)
  }
  // 把小组件的实例按照key存起来
  setVm(vm) {
    if (!!vm?.config?.key) {
      this.vms[vm.config.key] = vm
      this.vmOberver.publish(vm.config.key, vm)
    }
  }
  clearVm() {
    this.vms = {}
  }
  /**
   * 制定组件key加载完成后
   * @param {*} keys 数组 
   */
  readyComponent({ keys }) {
    let Pmall = keys?.map(key => {
      return new Promise((resolve, reject) => {
        if (!!this.vms[key]) {
          resolve()
        } else {
          this.vmOberver.subscribeready(key, vm => {
            resolve()
          })
        }
      })
    })
    return Promise.all(Pmall)
  }
}
/**
 * 页面初始化事件发布订阅
 */
class Oberver {
  constructor() {
    this.events = {}
    this.readyEvents = {}
  }
  /**
   * 事件名称订阅
   * @param {*} key 组件唯一key
   * @param {*} eventName  事件名称
   */
  subscribe(key, eventName, arg) {
    // 消息类型不存在
    if (typeof this.events[key] === 'undefined') {
      this.events[key] = [{ eventName, arg }]
      // 存在，将eventName推入事件队列
    } else {
      this.events[key].push({ eventName, arg })
    }
  }
  /**
   *
   * @param {*} key  组件key
   * @param {*} vm  组件实例
   * @returns
   */
  publish(key, vm) {
    // 消息类型没人订阅
    if (!!this.events[key]) {
      const Events = vm?.$options?.vmEvents || {}
      const CommonEvents = vm?.$options?.vmCommonEvents || {}
      const vmEvents = { ...Events, ...CommonEvents }
      this.events[key].forEach(({ eventName, arg }) => {
        vmEvents[eventName] && vmEvents[eventName].apply(vm, arg)
      })
    }
    if (!!this.readyEvents[key]) {
      this.readyEvents[key].forEach(({ callback }) => {
        callback.apply(vm)
      })
    }
  }
  subscribeready(key, callback) {
    // 消息类型不存在
    if (typeof this.readyEvents[key] === 'undefined') {
      this.readyEvents[key] = [{ callback }]
      // 存在，将eventName推入事件队列
    } else {
      this.readyEvents[key].push({ callback })
    }
  }
}
/**
 * js 脚本 工具类
 */
class Context {
  constructor({ template, vms, vmOberver, catchMap, pagekeys }) {
    this.vms = vms
    this.template = template
    this.vmOberver = vmOberver
    this.moment = require('moment')
    this.Message = Message
    this.MessageBox = MessageBox
    this.Loading = Loading
    this.cloneDeep = cloneDeep
    this.catchMap = catchMap
    this.md5 = require('md5')
    this.pagekeys = pagekeys
    this.$t = (key) => i18n.t(key)
    this.JsBarcode = jsbarcode
  }
  requestFullscreen(el) {
    document.body.requestFullscreen();
    el.classList.add("fullscreen");
  }
  async printPage(key, arg = {}) {
    let html = await postHmprinttemplatePreviewByKey(
      { showFrame: 0 },
      { key },
      arg
    );
    var iframe = document.createElement("IFRAME");
    var doc = null;
    iframe.setAttribute(
      "style",
      "position:absolute;width:0px;height:0px;left:-500px;top:-500px;"
    );
    document.body.appendChild(iframe);
    doc = iframe.contentWindow.document;
    doc.write(html);
    doc.close();
    iframe.onload = () => {
      iframe.contentWindow.onafterprint = null
      document.body.removeChild(iframe);
      doc = null
    };
    return new Promise((resolve, reject) => {
      iframe.contentWindow.onafterprint = () => {
        resolve()
      };
    })

  }
  /**
   * 全局request方法。可以用来向后端发起请求
   * @param {*} modelKey
   * @param {*} key
   * @param {*} options
   */
  async request(modelKey = '', key = '', options = {}) {
    return postHmhttprequestdefinitionExecute({
      modelKey,
      key,
      ...options
    })
  }

  async requestStream(modelKey = '', key = '', options = {}) {
    return postHmhttprequestdefinitionExecutestream({
      modelKey,
      key,
      ...options
    }, { responseType: 'blob' })
  }
  /**
   * 根据type获取list
   * @param {*} type 
   * @returns 
   */
  async dictionary(type) {
    if (!type.trim()) return []
    const res = await getHmdictionaryOptions({
      type,
    }, { useCache: true });
    return res.map((item) => {
      const { optionValue, optionName } = item;
      return {
        id: optionValue,
        label: optionName,
      };
    });
  }

  async hanCodeCall(httpServiceKey = '', serviceName = '', options = {}) {
    return postHmhttprequestdefinitionHancodecall({
      httpServiceKey,
      serviceName,
      ...options
    }, { useCache: true })
  }
  /**
   * 获取组件公用方法 getComponent(identity),根据组件唯一标识获取组件。
   * @param {*} key
   * @returns
   */
  getComponent(key) {
    if (this.pagekeys.indexOf(key) === -1) {
      // throw `组件标识【${key}】不存在`
      console.error(`组件标识【${key}】不存在`)
    }
    return new Component(key, this.vms, this.vmOberver)
  }
  /**
 * 异步获取组件公用方法 getComponent(identity),根据组件唯一标识获取组件。
 * @param {*} key
 * @returns
 */
  getComponentSync(key) {
    if (this.pagekeys.indexOf(key) === -1) {
      // throw `组件标识【${key}】不存在`
      console.error(`组件标识【${key}】不存在`)
    }
    if (!!this.vms[key]) {
      const vm = new Component(key, this.vms, this.vmOberver)
      return Promise.resolve(vm)
    }
    return new Promise((resolve, reject) => {
      this.vmOberver.subscribeready(key, () => {
        let vm = new Component(key, this.vms, this.vmOberver)
        resolve(vm)
      })
    })
  }
  /**
   * 获取窗口公用方法 getModal(identity),根据模态窗唯一标识获取模态窗
   */
  getModal(key) {
    let tem = this.template.find((i) => i.key === key)
    if (!tem) throw `模态框标识【${key}】不存在`
    const { FormData, name, formList, width, directionType, unit, direction, closeOnModal, dialogDrag } =
      tem || {}
    const instance = Modal.create(key)
    FormData.key = key
    const parentContent = this
    return {
      open({ title, onClose, onOpen } = {}) {
        instance.showModel(
          { closeOnModal, dialogDrag, direction, parentContent, directionType, formList, name: title || name, hmpageInfo: FormData, width: `${width}${unit || 'px'}` }, onClose, onOpen
        )
      },
      close(arg) {
        instance.closeModel(arg)
      }
    }
  }
  /**
   * 获取容器组件方法 getContainer(identity),根据容器唯一标识获取容器组件
   * @param {*} key
   */
  getContainer(key) { }
  /**
   * 获取当前用户信息 getCurrentUser(),返回当前用户信息包括用户id、用户账号、用户姓名、工号、手机号、所属部门、职位、邮箱
   */
  getCurrentUser() {
    const { id, username, name, jobNumber, mobile, orgList, position, email, isSuperAdmin } = store.getters.getUserVal
    return {
      id, username, name, jobNumber, mobile, orgList, position, email, isSuperAdmin
    }
  }
  /**
   *路由跳转 router(pageKey),跳转到指定key页面
   * @param {*} pageKey
   */
  router() {
    return new Router()
  }
  /**
   *getRouterParams(）获取当前页面参数
   */
  getRouterParams() {
    const { params, query } = router.currentRoute
    return cloneDeep({
      params,
      query
    })
  }

  downLoadStream(data, fileName) {
    downloadFileStream(data, fileName)
  }
}

/**
 * 获取组件实例公用方法
 */
class Component {
  constructor(key, vms, vmOberver) {
    this.vms = vms
    this.key = key
    this.vmOberver = vmOberver
    const vm = this.setEvents()
    if (!this.vmEvents) return
    for (const key in this.vmEvents) {
      this[key] = this.vmEvents[key].bind(vm)
    }
  }
  /**
   * 事件分发
   * @param {*} eventName  事件名称
   * @param  {...any} arg 参数
   * @returns
   */
  dispatch(eventName, ...arg) {
    // 重新获取vm实例确保调用时候拿到的是最新的实例  防止初始化的实例在异步事件内被调用
    const vm = this.setEvents()
    // 如果实例存在就同步调用
    const event = this.vmEvents?.[eventName]
    if (event) {
      return event.apply(vm, arg)
    } else {
      this.vmOberver.subscribe(this.key, eventName, arg)
    }
  }
  setEvents() {
    const vm = this.vms?.[this.key]
    const Events = vm?.$options?.vmEvents || {}
    const CommonEvents = vm?.$options?.vmCommonEvents || {}
    this.vmEvents = { ...Events, ...CommonEvents }
    return vm
  }
}
/**
 * 路由方法类
 */
class Router {
  push(pageKey, options = {}) {
    if (process.env.VUE_APP_USER === 'ADMIN') {
      Message.info('请在应用端预览')
      return
    }
    const { path, meta, query } = router.currentRoute
    const { title } = options
    const paremtMenuId = query.paremtMenuId || path
    const id = meta.id || pageKey
    router.push({
      name: 'app-id-uniiversal',
      query: {
        pageKey,
        paremtMenuId,
        title: title ? encodeURIComponent(title) : undefined,
        ...options.query
      },
      params: { id }
    })
  }
  replace(pageKey, options = {}) {
    if (process.env.VUE_APP_USER === 'ADMIN') {
      Message.info('请在应用端预览')
      return
    }
    const { path, meta, query } = router.currentRoute
    const { title } = options
    const paremtMenuId = query.paremtMenuId || path
    const id = meta.id || pageKey
    router.replace({
      name: 'app-id-uniiversal',
      query: {
        pageKey,
        paremtMenuId,
        title: title ? encodeURIComponent(title) : undefined,
        ...options.query
      },
      params: { id }
    })
  }
  async back() {
    const { path } = router.currentRoute
    vuexstore.dispatch('delVisitedViewByPath', path)
    router.back()
  }
  closeView() {
    const { path } = router.currentRoute
    vuexstore.dispatch('delVisitedViewByPath', path)
  }
  go(n) {
    router.go(n);
  }
  /**
   * 自定义流程跳转
   * @param {*} processKey   流程code
   * @param {*} options  跳转页面自定义配置
   */
  async startProcess(processKey, options = {}) {
    const pageKey = await getHmprocessStartformkey({ processKey })
    const { path, meta, query } = router.currentRoute
    const paremtMenuId = query.paremtMenuId || path
    const id = meta.id || pageKey
    const { title } = options
    router.push({
      name: 'app-id-uniiversal',
      title: title ? encodeURIComponent(title) : undefined,
      query: { pageKey, paremtMenuId, processKey, ...options.query },
      params: { id }
    })
  }
}
